<?php

namespace App\Services;

use App\Exceptions\ApiException;
use App\Exports\BookCatalog29;
use App\Exports\BookCatalog8;
use App\Exports\BookCatalog8JS;
use App\Exports\BookCatalogLY;
use App\Exports\BookCatalogBZ29;
use App\Exports\FullIndexExport;
use App\Jobs\SlowExeCatalog29;
use App\Jobs\SlowExeCatalog8;
use App\Jobs\SlowExeCatalog8JS;
use App\Jobs\SlowExeCatalogLY;
use App\Jobs\SlowExeCatalogBZ29;
use App\Jobs\SlowExeExportFullIndex;
use App\Models\BookCatalog;
use App\Models\DownloadJob;
use App\Models\DownloadJobQueue;
use App\Models\PartCompany;
use App\Models\Project;
use App\Models\ProjectClassify;
use App\Services\Utils\VerifyDate;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\Storage;
use PHPUnit\Util\Exception;

/**
 * 卷内目录服务层
 * Class BookCatalogService
 * @package App\Services
 */
class BookCatalogService extends BaseService
{
    /**
     * 防止用户多次点击导出导出卷内/全引同样内容
     *
     * @author: lxk
     * @date: 2024/7/19 14:17
     */
    public static function checkIsNeedExport($partCompanyId, $projectClassifyId, $taskName, $bookTitleIds)
    {
        return DownloadJob::query()
            ->where('user_id', user_id())
            ->where('part_company_id', $partCompanyId)
            ->where('project_classify_id', $projectClassifyId)
            ->where('task_name', $taskName)
            ->where('book_title_ids', implode(',', $bookTitleIds)) //一模一样的数据
            ->where('status', 1) //还在导出
            ->whereBetween('created_at', [now()->subMinutes(15), now()]) //15分钟内
            ->first();
    }

    /**
     * 获取全部案卷id
     *
     * @param $partCompanyId
     * @param $projectClassifyId
     * @return mixed
     * @throws ApiException
     * @author: lxk
     * @date: 2024/7/31 17:23
     */
    public function getIdsAll($partCompanyId, $projectClassifyId)
    {
        return $this->getBookTitleInstance($partCompanyId, $projectClassifyId)->pluck('id')->toArray();
    }

    /**
     * 需要打包成zip文件
     *
     * @param Request $request
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     * @author: lxk
     * @date: 2025-05-21 11:32
     */
    public function createUnitProZip($partCompanyId, $projectClassifyId, $type = 1)
    {
        $isZip                = 1; //需要被压缩
        $isDisplay            = 0; //需要打包的excel文件不显示
        $redisKey             = ProjectClassifyService::getProjectClassifyKey($partCompanyId);
        $proClassIds          = $this->getAllChildIds($partCompanyId, $projectClassifyId);
        $fileCount            = 0; //需要生成几个单位工程的excel
        $projectClassifyArray = json_decode(redis()->get($redisKey), true);
        foreach ($proClassIds as $proClassId) {
            $fileName     = $this->getFileNameByProId($proClassId, $projectClassifyArray);
            $bookTitleIds = $this->getIds($partCompanyId, $proClassId);
            if ($bookTitleIds) {
                $fileCount++;
                if ($type == 1) { //卷内
                    (new BookCatalogService())->exportExcel($partCompanyId, $proClassId, $bookTitleIds, $fileName, $isZip, $isDisplay);
                } elseif ($type == 2) { //全引
                    (new BookCatalogService())->exportFullIndexExcel($partCompanyId, $proClassId, $bookTitleIds, $fileName, $isZip, $isDisplay);
                }
            }
        }
        $isDisplay = 1;
        $dirName = self::getBaseDirPathByProClassId($type);
        (new DownloadJobService())->addNewData($partCompanyId, $projectClassifyId, [], $dirName, $fileCount, $type, $isZip, $isDisplay); //添加到任务表
    }

    /**
     * 获取当前单位工程的全部下级
     *
     * @param $partCompanyId
     * @param $projectClassifyId
     * @return array
     * @author: lxk
     * @date: 2025-05-18 17:14
     */
    public function getAllChildIds($partCompanyId, $projectClassifyId) : array
    {
        $redisKey = "childrenBts:{$partCompanyId}:{$projectClassifyId}";
        $tables = redis()->smembers($redisKey); //获取当前参建单位下的案卷全部的分表
        $childIds = [];
        foreach ($tables as $table) {
            $info = CommonService::getTableParams($table);
            $childIds[] = $info['id2'];
        }
        sort($childIds);

        return $childIds;
    }

    /**
     * 根据单位工程id获取生成的excel文件名
     *
     * @param $proClassId
     * @param $data
     * @return string
     * @author: lxk
     * @date: 2025-05-18 16:51
     */
    public function getFileNameByProId($proClassId, $data) : string
    {
        $nowProClassInfo = $this->findInArray($data, 'id', $proClassId);  //获取当前信息
        $fileName = $nowProClassInfo['title'];
        if ($nowProClassInfo['parent_id']) { //如果有父级
            $previousLevel1 = $this->findInArray($data, 'classify_id', $nowProClassInfo['parent_id']);
            $fileName = "{$previousLevel1['title']}_{$fileName}";
            if ($previousLevel1['parent_id']) { //父级还有父级
                $previousLevel2 = $this->findInArray($data, 'classify_id', $previousLevel1['parent_id']);
                $fileName = "{$previousLevel2['title']}_{$fileName}";
                if ($previousLevel2['parent_id']) { //父级还有父级
                    $previousLevel3 = $this->findInArray($data, 'classify_id', $previousLevel2['parent_id']);
                    $fileName = "{$previousLevel3['title']}_{$fileName}";
                }
            }
        }
        return $fileName;
    }

    /**
     * 获取一个二维关联数组中，指定键名和键值的那组数据
     *
     * @param $array
     * @param $key
     * @param $value
     * @return null
     * @author: lxk
     * @date: 2025-05-18 17:33
     */
    public function findInArray($array, $key, $value) {
        foreach ($array as $item) {
            if (isset($item[$key]) && $item[$key] == $value) {
                return $item;
            }
        }
        return null; // 如果没有找到返回 null
    }

    /**
     * 递归方法，获取当前参建单位的全部父级
     *
     * @param $partCompanyId
     * @param $projectClassifyId
     * @return array
     * @throws ApiException
     * @author: lxk
     * @date: 2025-05-22 9:43
     */
    public function getIds($partCompanyId, $projectClassifyId)
    {
        $bookTitleIds = CommonService::getBookTitleInstance($partCompanyId, $projectClassifyId)->pluck('id');

        return $bookTitleIds->isNotEmpty() ? $bookTitleIds->toArray() : [];
    }


    /**
     * 导出数据的时候，必定携带一个单位工程id, 使用这个值作为文件夹的数据
     * 这里使用的是请求的project_classify_id， 因为用户导出的数据可能涉及到多个 project_classify_id， 但 请求的project_classify_id是唯一的，
     * 这样保证生成的文件在一个文件夹里面，可以进行zip
     *
     * @return string
     * @author: lxk
     * @date: 2025-05-23 16:54
     */
    public static function getBaseDirPathByProClassId($type = 1) : string
    {
        $partCompanyId     = request()->input('part_company_id');
        $projectClassifyId = request()->input('project_classify_id');
        $proClassName      = ProjectClassify::query()->where('id', $projectClassifyId)->value('title');
        $partCompany       = PartCompany::query()->where('id', $partCompanyId)->first();
        $projectCode       = Project::query()->where('id', $partCompany->project_id)->value('document_number');
        $typeName          = $type == 1 ? '卷内目录' : '全引目录';
        $baseDirPath       = "excels/【{$projectCode}】{$partCompany->title}_{$proClassName}_{$typeName}";

        CommonService::createDir($baseDirPath); //创建文件夹

        return $baseDirPath;
    }

    /**
     * 导出卷内目录
     * @author: lxk
     * @date: 2023/8/25 17:06
     */
    public function exportExcel($partCompanyId, $projectClassifyId, $bookTitleIds = null, $fileName = null, $isZip = 0, $isDisplay = 0)
    {
        $fileCount   = count($bookTitleIds); //会拆分成几个excel
        $baseDirPath = self::getBaseDirPathByProClassId();
        $bookTitleInstance = $this->getBookTitleInstance($partCompanyId, $projectClassifyId);
        $serialNos = CommonService::getChildSerialNos($bookTitleInstance, $bookTitleIds);
        if (empty($fileName)) {
            $dirName = $baseDirPath . "_案卷{$serialNos}";
        } else {
            $dirName = $baseDirPath . "/{$fileName}_案卷{$serialNos}的卷内"; //这里加了参建单位加了‘_卷内目录’是为了后续给打包的zip文件命名
        }
        $downloadJobId = (new DownloadJobService())->addNewData($partCompanyId, $projectClassifyId, $bookTitleIds, $dirName, $fileCount, 1, $isZip, $isDisplay); //添加到任务表excel
        CommonService::addProjectActionLog("导出卷内目录,excel文件数量{$fileCount}", 0, $partCompanyId, $projectClassifyId);
        foreach ($bookTitleIds as $key=>$bookTitleId) { //按照逻辑，必须先把数据存到redis再执行到处任务
            $data = [
                'user_id'             => user_id(),
                'download_job_id'     => $downloadJobId,
                'part_company_id'     => $partCompanyId,
                'project_classify_id' => $projectClassifyId,
                'book_title_ids'      => $bookTitleId,
                'file_name'           => $dirName . '/' . ($key + 1) . '.xlsx',
                'created_at'          => date('Y-m-d H:i:s'),
                'status'              => 0,
            ];
            DownloadJobQueue::query()->insert($data);
        }
        $firstIds        = $bookTitleIds[0]; //把第一个进行导出
        $firstChildScope = 1;
        $firstFileName   = $dirName . '/' . $firstChildScope . '.xlsx';
        $combinationId   = (new ProjectService())->getDossierCombination($partCompanyId, $projectClassifyId);
        switch ($combinationId) { //用的哪种卷内格式
            case 2: //普通8号文格式
                $projectId = PartCompany::query()->where('id', $partCompanyId)->value('project_id');
                if (in_array($projectId, [5,15,16])) { //石武、石太、京石卷内目录格式
                    $fileCount == 1
                        ? (new BookCatalog8JS($partCompanyId, $projectClassifyId, $firstIds))->store('public/' . $firstFileName)
                        : (new BookCatalog8JS($partCompanyId, $projectClassifyId, $firstIds))->store('public/' . $firstFileName)->chain([new SlowExeCatalog8JS($downloadJobId)]);
                } else {
                    $fileCount == 1
                        ? (new BookCatalog8($partCompanyId, $projectClassifyId, $firstIds))->store('public/' . $firstFileName)
                        : (new BookCatalog8($partCompanyId, $projectClassifyId, $firstIds))->store('public/' . $firstFileName)->chain([new SlowExeCatalog8($downloadJobId)]);
                }
                break;
            case 3: //兰渝格式
                $fileCount == 1
                    ? (new BookCatalogLY($partCompanyId, $projectClassifyId, $firstIds))->store('public/' . $firstFileName)
                    : (new BookCatalogLY($partCompanyId, $projectClassifyId, $firstIds))->store('public/' . $firstFileName)->chain([new SlowExeCatalogLY($downloadJobId)]);
                break;
            case 4: //标准29号文
                $fileCount == 1
                    ? (new BookCatalogBZ29($partCompanyId, $projectClassifyId, $firstIds))->store('public/' . $firstFileName)
                    : (new BookCatalogBZ29($partCompanyId, $projectClassifyId, $firstIds))->store('public/' . $firstFileName)->chain([new SlowExeCatalogBZ29($downloadJobId)]);
                break;
            default: //经典29号文
                $fileCount == 1
                    ? (new BookCatalog29($partCompanyId, $projectClassifyId, $firstIds))->store('public/' . $firstFileName)
                    : (new BookCatalog29($partCompanyId, $projectClassifyId, $firstIds))->store('public/' . $firstFileName)->chain([new SlowExeCatalog29($downloadJobId)]);
                break;
        }
    }

    /**
     * 导出全引目录
     * @author: lxk
     * @date: 2023/8/25 17:06
     */
    public function exportFullIndexExcel($partCompanyId, $projectClassifyId, $bookTitleIds, $fileName, $isZip, $isDisplay)
    {
        $eachChunk   = 1;
        $arrChunks   = array_chunk($bookTitleIds, $eachChunk);  //选中的book_title_ids分割，每部分几个元素
        $fileCount   = ceil(count($bookTitleIds) / $eachChunk); //会拆分成几个excel
        $baseDirPath = self::getBaseDirPathByProClassId(2);
        $bookTitleInstance = $this->getBookTitleInstance($partCompanyId, $projectClassifyId);
        $serialNos   = CommonService::getChildSerialNos($bookTitleInstance, $bookTitleIds);
        if (empty($fileName)) {
            $dirName = $baseDirPath . "_案卷{$serialNos}";
        } else {
            $dirName = $baseDirPath . "/{$fileName}_案卷{$serialNos}的全引"; //这里加了参建单位加了‘_卷内目录’是为了后续给打包的zip文件命名
        }
        $downloadJobId = (new DownloadJobService())->addNewData($partCompanyId, $projectClassifyId, $bookTitleIds, $dirName, $fileCount, 2, $isZip, $isDisplay); //添加到任务表excel
        CommonService::addProjectActionLog("导出全引目录,excel文件数量{$fileCount}", 0, $partCompanyId, $projectClassifyId);
        foreach ($arrChunks as $key=>$arr) { //按照逻辑，必须先把数据存到redis再执行到处任务
            $data = [
                'user_id'             => user_id(),
                'download_job_id'     => $downloadJobId,
                'part_company_id'     => $partCompanyId,
                'project_classify_id' => $projectClassifyId,
                'book_title_ids'      => implode(',', $arr),
                'file_name'           => $dirName . '/' . ($key + 1) . '.xlsx',
                'created_at'          => date('Y-m-d H:i:s'),
                'status'              => 0,
            ];
            DownloadJobQueue::query()->insert($data);
        }
        $firstIds        = $arrChunks[0]; //把第一个进行导出
        $firstChildScope = 1;
        $firstFileName   = $dirName . '/' . $firstChildScope . '.xlsx';
        $fileCount == 1
            ? (new FullIndexExport($partCompanyId, $projectClassifyId, $firstIds, $firstChildScope))->store('public/' . $firstFileName)
            : (new FullIndexExport($partCompanyId, $projectClassifyId, $firstIds, $firstChildScope))->store('public/' . $firstFileName)->chain([new SlowExeExportFullIndex($downloadJobId)]);
    }


    /**
     * 添加卷内目录
     * $serialNo ：表示新增的这条数据序号的值
     */
    public function addNewData($params)
    {
        $bookCatalogInstance = $this->getBookCatalogInstance($params['part_company_id'], $params['project_classify_id']);
        $bookTitleId = $params['book_title_id'];
        if (request()->filled('id')) { //视为插入一行，插入到被选行的上方，然后更新当前案卷下的卷内序号
            $serialNo = $bookCatalogInstance->where('id', $params['id'])->value('serial_no');
            $bookCatalogInstance
                ->where('book_title_id', $bookTitleId)
                ->where('serial_no', '>=', $serialNo)
                ->increment('serial_no');
        } else { //获取本案卷最大的序号再+ 1
            $info     = $bookCatalogInstance->where('book_title_id', $bookTitleId)->latest('serial_no')->first();
            $serialNo = $info ? $info->serial_no + 1 : 1;
        }
        $bookCatalogInstance->book_title_id = $bookTitleId; //案卷目录 id
        $bookCatalogInstance->serial_no     = $serialNo; //序号
        $bookCatalogInstance->title         = '这是卷内标题'; //标题
        $bookCatalogInstance->file_number   = ''; //文件号
        $bookCatalogInstance->c_date        = ''; //日期
        $bookCatalogInstance->page_num      = ''; //页次
        $bookCatalogInstance->director      = ''; //责任人
        $bookCatalogInstance->remark        = ''; //备注
        $bookCatalogInstance->is_pic        = 0;
        $bookCatalogInstance->creator_id    = user_id();

        CommonService::addProjectActionLog("添加卷内目录", $bookCatalogInstance->id, $params['part_company_id'], $params['project_classify_id']);
        CommonService::isNeedUpdatePaperCount($params['part_company_id'], 1); //告诉参建单位需要更新下统计
        BookTitleService::updateCatalogCount($params['part_company_id'], $params['project_classify_id'], $bookTitleId); //更新案卷的卷内数量

        return $bookCatalogInstance->save();
    }

    /**
     * 更新 参数是一整行数据， 20220512-20220516
     */
    public function updateData($params)
    {
        $bookCatalogInstance = $this->getBookCatalogInstance($params['part_company_id'], $params['project_classify_id']);
        $bookTitleInstance   = $this->getBookTitleInstance($params['part_company_id'], $params['project_classify_id']);

        $date = format_str($params['c_date']);
        if (!VerifyDate::check($params['c_date'])) $date = ''; //如果日期不合法，不保存

        $bookCatalog              = $bookCatalogInstance->find($params['id']);
        $bookCatalog->serial_no   = to_int($params['serial_no']); //排序
        $bookCatalog->file_number = format_str($params['file_number']); //文件号
        $bookCatalog->c_date      = $date ?? ''; //日期
        $bookCatalog->title       = format_str($params['title']); //标题
        $bookCatalog->page_num    = format_page_num($params['page_num']); //页次
        $bookCatalog->director    = format_str($params['director']); //责任者
        $bookCatalog->remark      = format_str($params['remark']); //备注
        $bookCatalog->is_pic      = to_int($params['is_pic']); //备注
        $bookCatalog->creator_id  = user_id(); //操作人

        $bookTitleInstance->where('id', $bookCatalog->book_title_id)->update(['is_need_check' => 1]);

        CommonService::addProjectActionLog("更新卷内,案卷id={$bookCatalog->book_title_id},卷内id={$params['id']}", $params['id'], $params['part_company_id'], $params['project_classify_id']);

        return $bookCatalog->save();
    }

    /**
     * 获取数据列表 （根据 案卷目录id查找对应的卷内目录）
     * 卷内目录不做分页，因为数据最多也就数百条
     */
    public function getList($partCompanyId, $projectClassifyId, $bookTitleId, $keyword)
    {
        $bookCatalogInstance    = $this->getBookCatalogInstance($partCompanyId, $projectClassifyId);
        $bookCatalogPdfInstance = $this->getBookCatalogPdfInstance($partCompanyId, $projectClassifyId);

        $thisQuery = $bookCatalogInstance
            ->where('book_title_id', $bookTitleId)
            ->select("book_catalogs_{$partCompanyId}_{$projectClassifyId}.*")
            ->orderBy('serial_no');

        if (!empty($keyword)) {
            $thisQuery->where('title', 'like', "%{$keyword}%");
        }
        $list = $thisQuery->get();

        if ($list->isEmpty()) return [];

        $list = $list->makeHidden(['created_at', 'updated_at', 'deleted_at']); //隐藏多余字段
        $list = $list->toArray();
        $serialNoIds = [];
        foreach ($list as &$val) { //格式部分数据
            $val['file_url'] = 0;
            $val['number']   = $val['serial_no'];
            $val['title']    = htmlspecialchars_decode($val['title']);
            if (str_contains($val['title'], '&quot;')) {
                $bookCatalogInstance->where('id', $val['id'])->update(['title'=>format_str($val['title'])]);
            }
            $val['title'] = format_str($val['title']);
            $data = $bookCatalogPdfInstance->where('book_catalog_id', $val['id'])->first();
            if (!empty($data)) $val['file_url'] = 1;
            $tmp['id'] = $val['id'];
            $tmp['serial_no'] = $val['serial_no'];
            $serialNoIds[] = $tmp;
        }
        redis()->setex("serialNoIds:{$partCompanyId}:$projectClassifyId}:{$bookTitleId}", 600, json_encode($serialNoIds)); //临时存起来，用于重新排序

        return $list;
    }

    /**
     * 获取卷内的pdf列表
     */
    public function getPdfList($partCompanyId, $projectClassifyId, $bookCatalogId)
    {
        $bookCatalogPdfInstance = $this->getBookCatalogPdfInstance($partCompanyId, $projectClassifyId);
        $list = $bookCatalogPdfInstance
            ->where('book_catalog_id', $bookCatalogId)
            ->select('id', 'file_url', 'origin_name')
            ->get()
            ->toArray();

        foreach ($list as $key => &$val) {
            $count = $key + 1;
            $val['title'] = "【第{$count}个】" . $val['origin_name'];
        }
        return $list;
    }

    /**
     * 批量替换
     */
    public function batchReplace($params)
    {
        $partCompanyId     = $params['part_company_id']; //参见单位id
        $projectClassifyId = $params['project_classify_id']; //参见单位id
        $type              = $params['type']; //
        $bookTitleId       = $params['book_title_id'];
        $beforeStr         = $params['before_str']; //需替换字符
        $afterStr          = $params['after_str']; //替换为

        $bookCatalogInstance = $this->getBookCatalogInstance($partCompanyId, $projectClassifyId);
        $bookCatalogIds      = to_array($params['book_catalog_ids']);
        if (empty($params['book_catalog_ids'])) { //如果没指定，就是全部卷内
            $bookCatalogIds = $bookCatalogInstance->where('book_title_id', $bookTitleId)->pluck('id')->toArray();
        }
        $baseQuery = $bookCatalogInstance
            ->whereIn('id', $bookCatalogIds)
            ->when($type == 1,  function ($query) use ($beforeStr) {
                $query->where('file_number', 'like', "%{$beforeStr}%");
            })
            ->when($type == 2,  function ($query) use ($beforeStr) {
                $query->where('director', 'like', "%{$beforeStr}%");
            })
            ->when($type == 3,  function ($query) use ($beforeStr) {
                $query->where('title', 'like', "%{$beforeStr}%");
            })
            ->select('id', 'file_number', 'director', 'title')
            ->orderBy('id');

        $baseQuery->chunk(1000, function ($catalogs) use ($type, $beforeStr, $afterStr) {
            foreach ($catalogs as $catalog) {
                if ($type == 1) {
                    $fileNumber = str_replace($beforeStr, $afterStr, $catalog->file_number);
                    $catalog->update(['file_number' => $fileNumber]);
                } elseif ($type == 2) {
                    $director = str_replace($beforeStr, $afterStr, $catalog->director);
                    $catalog->update(['director' => $director]);
                } else {
                    $title = str_replace($beforeStr, $afterStr, $catalog->title);
                    $catalog->update(['title' => $title]);
                }
            }
        });
        $idsStr = implode(',', $bookCatalogIds);
        CommonService::addProjectActionLog("批量替换卷内:把{$beforeStr}替换为{$afterStr}", $idsStr, $partCompanyId, $projectClassifyId);
    }

    /**
     * 格式化批量的file_urls
     * 意味着指定案卷目录下的，第几个序号的PDF文件
     */
    private function formatFileUrls($fileUrls)
    {
        foreach ($fileUrls as &$val) { //'origin_name' => 'J•SJKZ•5ZH123169-1-1'
            $pos                = strrpos($val['origin_name'], '-'); //最后'-'出现的位置
            $mainArchivesNo     = substr($val['origin_name'], 0, $pos);
            $index              = substr($val['origin_name'], $pos + 1); //截取序号
            $val['archives_no'] = $mainArchivesNo;
            $val['index']       = $index;
        }

        return $fileUrls;
    }

    /**
     * 检查卷内目录数据合法性
     */
    public function checkBookCatalogData($bookTitleInstance, $bookCatalogInstance, $bookTitleId, $combinationId)
    {
        switch ($combinationId) { //用的哪种卷内格式
            case 2: //8号文
                $this->checkData8($bookTitleInstance, $bookCatalogInstance, $bookTitleId);
                break;
            case 3: //兰渝格式
                $this->checkDataLY($bookTitleInstance, $bookCatalogInstance, $bookTitleId);
                break;
            default: //29号文
                $this->checkData29($bookTitleInstance, $bookCatalogInstance, $bookTitleId);
                break;
        }
    }

    /**
     * 检查数据合法性【8号文】
     */
    public function checkData8($bookTitleInstance, $bookCatalogInstance, $bookTitleId)
    {
        $list = $bookCatalogInstance
            ->where('book_title_id', $bookTitleId)
            ->select('id', 'serial_no', 'file_number', 'title', 'page_num', 'problem')
            ->orderBy('serial_no')
            ->get();

        $catalogRedisKey = get_rand_str('is_catalog_has_problem'); //记录卷内是否有异常，入股卷内没有异常，同时所属案卷也没异常，则去掉案卷的标记
        redis()->setex($catalogRedisKey, 300, 0); //初始值为0，有效期300秒

        if ($list->isNotEmpty()) {
            $catalogs = $list->toArray();

            $pageNums = array_column($catalogs, 'page_num'); //检查页号是否从小到大
            $this->checkPageNums($pageNums, $bookTitleInstance, $bookCatalogInstance, $bookTitleId, $catalogRedisKey);

            $serialNos = array_column($catalogs, 'serial_no');
            if (!$this->checkSerialNo($serialNos)) {
                redis()->incrby($catalogRedisKey, 1);
                BookTitleService::problemMark($bookTitleInstance, to_array($bookTitleId), '卷内数据序号不合法');

                $minSerialNo = min($serialNos); //把卷内数据也标记，由于情况太多，卷内序号不合法统一标记到最小的那个序号
                $id = $bookCatalogInstance->where('book_title_id', $bookTitleId)->where('serial_no', $minSerialNo)->value('id');
                self::problemMark($bookCatalogInstance, $id, '卷内序号不合法，必须从1开始连续自增且不得重复！', $bookTitleInstance, $bookTitleId);
            }
            foreach ($catalogs as $val) {
                $id = to_array($val['id']);
                if (!$val['title']) {
                    redis()->incrby($catalogRedisKey, 1);
                    self::problemMark($bookCatalogInstance, $id, '文件材料题名不能为空', $bookTitleInstance, $bookTitleId);
                }
                if (!CommonService::checkPageNum($val['page_num'])) {
                    redis()->incrby($catalogRedisKey, 1);
                    self::problemMark($bookCatalogInstance, $id, '页号不合法', $bookTitleInstance, $bookTitleId);
                }
                if (!$val['page_num']) {
                    redis()->incrby($catalogRedisKey, 1);
                    self::problemMark($bookCatalogInstance, $id, '没有页号', $bookTitleInstance, $bookTitleId);
                }
                if (!redis()->get($catalogRedisKey)) { //如果卷内没异常，去掉卷内标记
                    self::removeMark($bookCatalogInstance, $id, $bookTitleInstance, $bookTitleId);
                }
            }
            $this->updateBookTitle8($bookTitleInstance, $bookTitleId, $catalogs, $catalogRedisKey); //如果一切正常，更新案卷目录的页次和日期字段
        }
    }

    /**
     * 检查数据合法性【29号文】
     */
    public function checkData29($bookTitleInstance, $bookCatalogInstance, $bookTitleId)
    {
        $list = $bookCatalogInstance
            ->where('book_title_id', $bookTitleId)
            ->select('id', 'serial_no', 'title', 'book_title_id', 'director', 'c_date', 'page_num', 'file_url', 'is_pic', 'problem')
            ->orderBy('serial_no')
            ->get();

        $catalogRedisKey = get_rand_str('is_catalog_has_problem'); //记录卷内是否有异常
        redis()->setex($catalogRedisKey, 300, 0); //初始值为0，有效期300秒

        if ($list->isNotEmpty()) {
            $catalogs = $list->toArray();
            /**
             * 检查卷内序号是否从小到大并唯一
             */
            $serialNos = array_column($catalogs, 'serial_no');
            if (!$this->checkSerialNo($serialNos)) {
                redis()->incrby($catalogRedisKey, 1);
                BookTitleService::problemMark($bookTitleInstance, to_array($bookTitleId), '卷内【序号不合法】');
                $minSerialNo = min($serialNos); //把卷内数据也标记，由于情况太多，卷内序号不合法统一标记到最小的那个序号
                $id = $bookCatalogInstance->where('book_title_id', $bookTitleId)->where('serial_no', $minSerialNo)->value('id');
                self::problemMark($bookCatalogInstance, $id, '卷内序号不合法，必须从1开始连续自增且不得重复！', $bookTitleInstance, $bookTitleId);
            }
            /**
             * 检查卷内页码是否从小到大
             */
            $pageNums = array_column($catalogs, 'page_num');
            $this->checkPageNums($pageNums, $bookTitleInstance, $bookCatalogInstance, $bookTitleId, $catalogRedisKey);

            foreach ($catalogs as $val) {
                $id = to_array($val['id']);
                if (!$val['title']) {
                    redis()->incrby($catalogRedisKey, 1);
                    self::problemMark($bookCatalogInstance, $id, '文件材料题名为空', $bookTitleInstance, $bookTitleId);
                }
                if (!$val['director']) {
                    redis()->incrby($catalogRedisKey, 1);
                    self::problemMark($bookCatalogInstance, $id, '责任者为空', $bookTitleInstance, $bookTitleId);
                }
                if (!CommonService::checkPageNum($val['page_num'])) {
                    redis()->incrby($catalogRedisKey, 1);
                    self::problemMark($bookCatalogInstance, $id, '页号不合法', $bookTitleInstance, $bookTitleId);
                }
                if (!VerifyDate::check($val['c_date'])) {
                    redis()->incrby($catalogRedisKey, 1);
                    self::problemMark($bookCatalogInstance, $id, '日期不合法', $bookTitleInstance, $bookTitleId);
                }
                if (!redis()->get($catalogRedisKey)) { //如果卷内没异常，去掉卷内标记
                    self::removeMark($bookCatalogInstance, $id, $bookTitleInstance, $bookTitleId);
                }
            }
            $this->updateBookTitle29($bookTitleInstance, $bookTitleId, $catalogs, $catalogRedisKey); //更新案卷目录的页次和日期字段，要求检查完一个，就更新一个
        }
    }

    /**
     * 检查数据合法性【兰渝格式】
     */
    public function checkDataLY($bookTitleInstance, $bookCatalogInstance, $bookTitleId)
    {
        $list = $bookCatalogInstance
            ->where('book_title_id', $bookTitleId)
            ->select('id', 'serial_no', 'title', 'book_title_id', 'director', 'c_date', 'page_num', 'problem')
            ->orderBy('serial_no')
            ->get();

        $catalogRedisKey = get_rand_str('is_catalog_has_problem'); //记录卷内是否有异常
        redis()->setex($catalogRedisKey, 300, 0); //初始值为0，有效期300秒

        if ($list->isNotEmpty()) {
            $catalogs  = $list->toArray();
            /**
             * 检查卷内序号是否从小到大并唯一
             */
            $serialNos = array_column($catalogs, 'serial_no');
            if (!$this->checkSerialNo($serialNos)) {
                redis()->incrby($catalogRedisKey, 1);
                BookTitleService::problemMark($bookTitleInstance, to_array($bookTitleId), '卷内【序号不合法】');
                $minSerialNo = min($serialNos); //把卷内数据也标记，由于情况太多，卷内序号不合法统一标记到最小的那个序号
                $id = $bookCatalogInstance->where('book_title_id', $bookTitleId)->where('serial_no', $minSerialNo)->value('id');
                self::problemMark($bookCatalogInstance, $id, '卷内序号不合法，必须从1开始连续自增且不得重复！', $bookTitleInstance, $bookTitleId);
            }
            /**
             * 检查卷内页码是否从小到大
             */
            $pageNums = array_column($catalogs, 'page_num');
            $this->checkPageNums($pageNums, $bookTitleInstance, $bookCatalogInstance, $bookTitleId, $catalogRedisKey);

            foreach ($catalogs as $val) {
                $id = to_array($val['id']);
                if (!$val['title']) {
                    redis()->incrby($catalogRedisKey, 1);
                    self::problemMark($bookCatalogInstance, $id, '文件材料题名为空', $bookTitleInstance, $bookTitleId);
                }
                if (!CommonService::checkPageNum($val['page_num'])) {
                    redis()->incrby($catalogRedisKey, 1);
                    self::problemMark($bookCatalogInstance, $id, '页码不合法', $bookTitleInstance, $bookTitleId);
                }
                if (!redis()->get($catalogRedisKey)) { //如果卷内没异常，去掉卷内标记
                    self::removeMark($bookCatalogInstance, $id, $bookTitleInstance, $bookTitleId);
                }
            }
            $this->updateBookTitleLY($bookTitleInstance, $bookTitleId, $catalogs, $catalogRedisKey); //更新案卷目录的页次和日期字段，要求检查完一个，就更新一个
        }
    }

    /**
     * 判断序号是否连续并从1递增，序号不符合要求，导出的卷内会出错
     *
     * @param $serialNos
     * @return bool
     * @author: lxk
     * @date: 2024/3/9 14:15
     */
    public function checkSerialNo($serialNos) : bool
    {
        if (min($serialNos) != 1) {
            return false;
        }
        if (max($serialNos) != count($serialNos)) {
            return false;
        }
        if (sort($serialNos) != $serialNos) {
            return false;
        }
        if (array_unique($serialNos) != $serialNos) {
            return false;
        }

        return true;
    }

    /**
     * 检查卷内页码是否从小到大
     * @author: lxk
     * @date: 2024/3/9 15:05
     */
    public function checkPageNums($pageNums, $bookTitleInstance, $bookCatalogInstance, $bookTitleId, $catalogRedisKey)
    {
        $endPageNum = end($pageNums);
        if (!str_contains($endPageNum, '～')) {
            $bookCatalogInstance->where('book_title_id', $bookTitleId)->where('page_num', $endPageNum)->update(['problem'=>'页号不合法']);
            redis()->incrby($catalogRedisKey, 1);
            BookTitleService::problemMark($bookTitleInstance, to_array($bookTitleId), '卷内【页号不合法】');
        }
        foreach ($pageNums as $key=>$pageNum) {
            if ($key > 0) {
                $prePageNum = $pageNums[$key-1]; //上个页码
                $nowPageNum = CommonService::getPageInt($pageNum); //当前页码,如果是 1～119 这种格式，就取 1
                if (is_numeric($prePageNum) && $prePageNum >= $nowPageNum) { //有问题
                    $bookCatalogInstance->where('book_title_id', $bookTitleId)->where('page_num', $prePageNum)->update(['problem'=>'页号不合法']);
                    redis()->incrby($catalogRedisKey, 1);
                    BookTitleService::problemMark($bookTitleInstance, to_array($bookTitleId), '卷内【页号不合法】');
                }
            } else { //对第一个页号进行判断
                $haveProblem = false;
                if (is_numeric($pageNums[$key])) { //如果是数字
                    if ($pageNums[$key] != 1)  $haveProblem = true;
                } else {
                    if (!$pos = strpos($pageNum, '～')) {
                        $haveProblem = true;
                    } else {
                        $smallPage = mb_substr($pageNum, 0, $pos);
                        if ($smallPage != 1) {
                            $haveProblem = true;
                        } else {
                            $result = json_encode(mb_substr($pageNum, $pos + 1));
                            if (!empty($result)) {
                                $bigPage = mb_substr($pageNum, $pos + 1);
                            } else {
                                $bigPage = mb_substr($pageNum, $pos + 3);
                            }
                            if (!is_numeric($smallPage) || !is_numeric($bigPage)) {
                                $haveProblem = true;
                            }
                            if ($smallPage > $bigPage) {
                                $haveProblem = true;
                            }
                        }
                    }
                }
                if ($haveProblem) {
                    $bookCatalogInstance->where('book_title_id', $bookTitleId)->where('page_num', $pageNums[$key])->update(['problem' => '页号不合法']);
                    redis()->incrby($catalogRedisKey, 1);
                    BookTitleService::problemMark($bookTitleInstance, to_array($bookTitleId), '卷内【页号不合法】');
                }
            }
        }
    }

    /**
     * 获取一个案卷的卷内最大页码
     */
    public static function getMaxPageNum($partCompanyId, $projectClassifyId, $bookTitleId)
    {
        $bookCatalogInstance = (new BaseService())->getBookCatalogInstance($partCompanyId, $projectClassifyId);
        $info = $bookCatalogInstance
            ->where('book_title_id', $bookTitleId)
            ->orderByDesc('serial_no')
            ->first();
        $pageNum = $info->page_num;

        if (!str_contains($pageNum, '～')) return false; //如果序号最大的卷内页码不包含 '～' ，认为是不合法

        $pos    = strrpos($pageNum, '～');
        $result = json_encode(mb_substr($pageNum, $pos + 1));
        if (!empty($result)) {
            $maxPageNum = mb_substr($pageNum, $pos + 1);
        } else {
            $maxPageNum = mb_substr($pageNum, $pos + 3);
        }
        return $maxPageNum;
    }


    /**
     * 获取一个案卷里面的异常卷内数量
     */
    public static function getProblemCatalogCount($bookCatalogInstance, $bookTitleId)
    {
        return $bookCatalogInstance->where('book_title_id', $bookTitleId)->where('problem', '!=', '')->count();
    }

    /**
     * 卷内问题标记，如果卷内有问题，所属案卷也进行标记
     */
    public static function problemMark($bookCatalogInstance, $bookCatalogId, $problem, $bookTitleInstance, $bookTitleId)
    {
        $bookTitleInstance->where('id', $bookTitleId)->update(['problem' => '卷内数据异常']);
        $bookCatalogInstance->where('id', $bookCatalogId)->update(['problem' => $problem]);
    }

    /**
     * 移除问题标记，如果卷内有问题，所属案卷也进行标记
     */
    public static function removeMark($bookCatalogInstance, $bookCatalogId, $bookTitleInstance, $bookTitleId)
    {
        $bookTitleInstance->where('id', $bookTitleId)->update(['problem' => '']);
        $bookCatalogInstance->where('id', $bookCatalogId)->update(['problem' => '']);
    }

    /**
     * 检查完毕后更新一条案卷信息
     */
    public function updateBookTitleLY($bookTitleInstance, $bookTitleId, $list, $catalogRedisKey)
    {
        $hadProblem = redis()->get($catalogRedisKey);
        if (empty($hadProblem)) {
            $pageNumDataArr = BookTitleService::getSerialNoPageNum($list);
            $bookTitleInstance->where('id', $bookTitleId)->update([
                'page_num'      => array_sum(array_values($pageNumDataArr)), //页次
                'is_need_check' => 0, //如果没有异常，不再检查
            ]);
        }
    }

    /**
     * 检查完毕后更新一条案卷信息
     */
    public function updateBookTitle29($bookTitleInstance, $bookTitleId, $list, $catalogRedisKey)
    {
        $hadProblem = redis()->get($catalogRedisKey);
        if (empty($hadProblem)) {
            //日期
            $noUpdate   = 0;
            $newDateArr = [];
            $dateArr    = array_column($list, 'c_date');
            foreach ($dateArr as $date) {
                if (!VerifyDate::check($date)) { //如果有日期不合法的，不更新案卷日期
                    $noUpdate = 1;
                    break; //终止foreach
                }
                if (!empty($date)) {
                    if (strlen($date) > 15) { //能判定是20180919～20190315这格式即可
                        $res = VerifyDate::check($date);
                        $newDateArr[] = $res['start_date'];
                        $newDateArr[] = $res['end_date'];
                    } else {
                        $newDateArr[] = $date;
                    }
                };
            }
            //页次
            $pageNumDataArr = BookTitleService::getSerialNoPageNum($list);
            $bookTitleInstance->where('id', $bookTitleId)->update([
                'start_date'    => $noUpdate ? '' : min($newDateArr),
                'end_date'      => $noUpdate ? '' : max($newDateArr),
                'page_num'      => array_sum(array_values($pageNumDataArr)), //页次
                'is_need_check' => 0, //如果没有异常，不再检查
            ]);
        }
    }

    /**
     * 检查完毕后更新一条案卷信息
     */
    public function updateBookTitle8($bookTitleInstance, $bookTitleId, $list, $catalogRedisKey)
    {
        $hadProblem = redis()->get($catalogRedisKey);
        if (empty($hadProblem)) {
            $pageNumDataArr = BookTitleService::getSerialNoPageNum($list);
            $bookTitleInstance->where('id', $bookTitleId)->update([
                'page_num' => array_sum(array_values($pageNumDataArr)),
            ]);
        }
    }


    /**
     * 单个卷内上传若干个PDF
     *
     * @return bool
     * @throws ApiException
     * @author: lxk
     * @date: 2024/3/19 8:52
     */
    public function addSomePdfs($partCompanyId, $projectClassifyId, $bookCatalogId, $uploadResult) : bool
    {
        $bookCatalogInstance = $this->getBookCatalogInstance($partCompanyId, $projectClassifyId);
        $info = $bookCatalogInstance->find($bookCatalogId);
        $info->creator_id = user_id();
        $info->file_url   = 1;
        $info->updated_at = now();
        $info->save();

        $fileUrl = $uploadResult['file'];
        $pdfName = format_archives_no($uploadResult['origin_name']);
        $bookCatalogPdfInstance = $this->getBookCatalogPdfInstance($partCompanyId, $projectClassifyId);
        $bookCatalogPdfInstance->insert([
            'book_title_id'   => $info->book_title_id,
            'book_catalog_id' => $bookCatalogId,
            'file_url'        => $fileUrl,
            'origin_name'     => $pdfName,
            'file_size'       => $uploadResult['file_size'],
            'created_at'      => now(),
            'creator_id'      => user_id(),
        ]);
        CommonService::addProjectActionLog("单条卷内上传PDF,卷内id={$bookCatalogId}", $bookCatalogId, $partCompanyId, $projectClassifyId);
        CommonService::isNeedUpdatePaperCount($partCompanyId, 1);

        return true;
    }

    /**
     * 删除卷内PDF
     *
     * @param $partCompanyId
     * @param $projectClassifyId
     * @param $pdfId
     * @return bool
     * @throws ApiException
     * @author: lxk
     * @date: 2024-09-13 15:19
     */
    public function delPdf($partCompanyId, $projectClassifyId, $pdfId) : bool
    {
        try {
            $catalogInstance    = $this->getBookCatalogInstance($partCompanyId, $projectClassifyId);
            $catalogPdfInstance = $this->getBookCatalogPdfInstance($partCompanyId, $projectClassifyId);

            $catalogPdf = $catalogPdfInstance->where('id', $pdfId)->first();
            $fileUrl    = $catalogPdf->file_url;
            $catalogId  = $catalogPdf->photo_catalog_id;

            $catalogPdf->where('id', $pdfId)->delete();
            $count = $catalogPdfInstance->where('book_catalog_id', $catalogId)->count();
            if (empty($count)) { //删除一张PDF后，判断这条卷内还有没有PDF，如果没有，则更新为0
                $catalogInstance->where('id', $catalogId)->update(['file_url' => 0]);
            }
            if (CommonService::isDelOssObject()) { //如果是正式服操作，从OSS里面删除
                (new AttachmentService())->delOssFile($fileUrl);
            }
            CommonService::addProjectActionLog('删除Pdf', $catalogId, $partCompanyId, $projectClassifyId);
            CommonService::isNeedUpdatePaperCount($partCompanyId, 1);

            return true;
        }
        catch (Exception $exception) {
            throw new ApiException($exception->getMessage());
        }
    }

    /**
     * 回收站列表
     */
    public function getRecycles($request): array
    {
        $thisQuery = BookCatalog::onlyTrashed();
        if ($request->filled('keyword')) { //判断参数是否存在并且不为空
            $keyword = $request->input('keyword');
            $thisQuery->where('title', 'like', "%{$keyword}%")->orWhere('id', '=', $keyword); //由于不同的表字段不同，所以单独拿出来
        }

        return $this->formatRecycleData($request, $thisQuery);
    }

    /**
     * 删除卷内的时候，如果有PDF，把关联的PDF也删除
     *
     * @param $partCompanyId
     * @param $projectClassifyId
     * @param $bookCatalogIds
     * @throws ApiException
     * @author: lxk
     * @date: 2024/4/9 10:15
     */
    public function delCatalogPdf($partCompanyId, $projectClassifyId, $bookCatalogIds)
    {
        if (CommonService::isDelOssObject()) { //正式服操作才删除PDF
            $pdfTable = "book_catalog_pdfs_{$partCompanyId}_{$projectClassifyId}";
            if (Schema::hasTable($pdfTable)) { //如果分表存在
                $pdfInstance = $this->getBookCatalogPdfInstance($partCompanyId, $projectClassifyId);
                $fileUrls    = $pdfInstance->whereIn('book_catalog_id', $bookCatalogIds)->pluck('file_url');
                if (!empty($fileUrls)) {
                    $fileUrls   = $fileUrls->toArray();
                    $attachment = new AttachmentService();
                    foreach ($fileUrls as $fileUrl) {
                        $attachment->delOssFile($fileUrl); //删除pdf
                    }
                }
                $pdfInstance->whereIn('book_catalog_id', $bookCatalogIds)->update([
                    'book_catalog_id' => 0,
                    'file_url'        => '',
                    'file_size'       => 0,
                    'origin_name'     => '',
                    'deleted_at'      => now(),
                ]);
            }
        }
    }

    /**
     * 删除若干数据后，剩余的数据序号和档案号重新排列，第一个序号为1 从小到大递增
     */
    public function delData($partCompanyId, $projectClassifyId, $bookTitleId, $bookCatalogIds)
    {
        $bookCatalogInstance = (new BaseService())->getBookCatalogInstance($partCompanyId, $projectClassifyId);
        if (!$bookCatalogInstance->whereIn('id', $bookCatalogIds)->delete()) {
            throw new ApiException('删除失败！');
        };
        $this->delCatalogPdf($partCompanyId, $projectClassifyId, $bookCatalogIds);
        $redisKey = get_rand_str('del_catalog');
        redis()->set($redisKey, 0); //定义个临时变量
        $bookCatalogInstance
            ->where('book_title_id', $bookTitleId)
            ->select('id', 'serial_no')
            ->orderBy('serial_no', 'asc')
            ->orderBy('id', 'asc')
            ->chunk(100, function ($bookCatalogs) use ($redisKey) {
                foreach ($bookCatalogs as $bookCatalog) {
                    redis()->incrby($redisKey, 1); //加1
                    $serialNo = redis()->get($redisKey); //获取增加后的值
                    $bookCatalog->where('id', $bookCatalog->id)->update(['serial_no'=> $serialNo]);
                }
            });
        redis()->del($redisKey); //用完删除
        $ids = implode(',', $bookCatalogIds);

        CommonService::addProjectActionLog("删除卷内", $ids, $partCompanyId, $projectClassifyId); //增加操作日志
        CommonService::isNeedUpdatePaperCount($partCompanyId, 1); //告诉参建单位需要更新下统计
        BookTitleService::updateCatalogCount($partCompanyId, $projectClassifyId, $bookTitleId); //更新案卷的卷内数量

        return true;
    }

    /**
     * 将涉及到的案卷目录页数获取，生成以档案号命名的txt文件，文件内容是
     */
    public function exportPageNum($partCompanyId, $projectClassifyId, $ids, $catalogAtHead, $appendixAtEnd)
    {
        $bookTitleInstance   = $this->getBookTitleInstance($partCompanyId, $projectClassifyId);
        $bookCatalogInstance = $this->getBookCatalogInstance($partCompanyId, $projectClassifyId);
        $bookTitleList = $bookTitleInstance
            ->where('project_classify_id', $projectClassifyId)
            ->when($ids, function($query) use ($ids) {
                return $query->whereIn('id', to_array($ids));
            })
            ->select('id', 'archives_no')
            ->orderBy('id')
            ->get();

        if ($bookTitleList->isEmpty()) throw new ApiException('暂无案卷数据！');

        $bookTitleList   = $bookTitleList->toArray();
        $bookTitleIds    = array_column($bookTitleList, 'id');
        $bookCatalogList = $bookCatalogInstance
            ->whereIn('book_title_id', $bookTitleIds)
            ->orderBy('book_title_id')
            ->orderBy('serial_no')
            ->select('id', 'serial_no','book_title_id', 'page_num')
            ->get();
        if ($bookCatalogList->isEmpty()) throw new ApiException('暂无卷内数据！');

        $bookCatalogList = $bookCatalogList->toArray();
        $serialNos       = CommonService::getChildSerialNos($bookTitleInstance, to_array($bookTitleIds));
        $partCompanyName = PartCompany::query()->where('id', $partCompanyId)->value('title');
        $projectClassifyName = ProjectClassify::query()->where('id', $projectClassifyId)->value('title');
        $zipName = '卷内页码_' . $partCompanyName . '_' . $projectClassifyName . '_' . $serialNos;
        $zipName = get_valid_path($zipName);
        foreach ($bookTitleList as $bookTitle) {
            $tmp1   = [];
            $result = [];
            foreach ($bookCatalogList as $bookCatalog) {
                if ($bookTitle['id'] == $bookCatalog['book_title_id']) {
                    $pageNum = $bookCatalog['page_num'];
                    if (empty($pageNum)) continue;
                    if (is_numeric($pageNum)) {
                        $tmp1[] = (int)$pageNum;
                    } else {
                        $pageNum = self::getSmallAndBigPage($pageNum);
                        $tmp1[]  = $pageNum['small_page'];
                        $tmp1[]  = $pageNum['big_page'];
                    }
                }
            }
            $catalogPageCount = 0;
            if ($catalogAtHead) { //如果开头有卷内
                $catalogCount = $bookCatalogInstance->where('book_title_id', $bookTitle['id'])->count();
                $catalogPageCount = ceil($catalogCount / 15); //剔除开头的卷内目录页码
            }
            array_shift($tmp1);
            for ($i = 0; $i < count($tmp1); $i++) {
                if ($tmp1[$i] >= 1) {
                    $result[] = ($tmp1[$i] - 1) + $catalogPageCount;
                }
            }
            $endVal = end($result) + 1; //最后一个值加1
            array_pop($result); //删除最后一个值
            array_push($result, $endVal); //再追击一个值
            if ($appendixAtEnd) { //如果有备考表，末尾再加个值
                $endVal = $endVal + 1;
                array_push($result, $endVal); //再追击一个值
            }
            $pageNumStr   = implode(',', $result);
            $archivesNo   = $bookTitle['archives_no'];
            if (str_contains($archivesNo, '/')) {
                $archivesNo = str_replace('/', '--', $archivesNo);
            }
            if ($catalogAtHead) { //如果开头有卷内
                Storage::put("public/pageTxts/{$zipName}/{$archivesNo}.txt", $catalogPageCount.','.$pageNumStr); //将字符串写入文本
            } else {
                Storage::put("public/pageTxts/{$zipName}/{$archivesNo}.txt", $pageNumStr); //将字符串写入文本
            }
        }
        return CommonService::createZip("storage/pageTxts/{$zipName}");
    }

    /**
     * 获取非数字页码的大页码和小页码
     */
    public static function getSmallAndBigPage($pageNum)
    {
        $pos       = strpos($pageNum, '～');
        $smallPage = mb_substr($pageNum, 0, $pos);
        $result    = json_encode(mb_substr($pageNum, $pos + 1));
        if (!empty($result)) {
            $bigPage = mb_substr($pageNum, $pos + 1);
        } else {
            $bigPage = mb_substr($pageNum, $pos + 3);
        }
        return [
            'small_page' => (int)$smallPage,
            'big_page'   => (int)$bigPage
        ];
    }

}
